package com.swanclouds.redfish.internal;

import com.google.common.annotations.VisibleForTesting;
import com.swanclouds.api.Apis;
import com.swanclouds.api.CloudProvider;
import com.swanclouds.api.EndpointTokenProvider;
import com.swanclouds.api.RedfishClient;
import com.swanclouds.api.account.AccountService;
import com.swanclouds.api.chassis.ChassisResourceService;
import com.swanclouds.api.event.EventResourceService;
import com.swanclouds.api.identity.EndpointURLResolver;
import com.swanclouds.api.managers.ManagerResourceSerice;
import com.swanclouds.api.session.SessionService;
import com.swanclouds.api.systems.SystemResourceService;
import com.swanclouds.api.task.TaskResourceService;
import com.swanclouds.api.types.ServiceType;
import com.swanclouds.api.updates.UpdateService;
import com.swanclouds.core.transport.Config;
import com.swanclouds.model.identity.Token;
import com.swanclouds.model.identity.URLResolverParams;
import com.swanclouds.model.identity.internal.DefaultEndpointURLResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Set;

/**
 * A client which has been identified. Any calls spawned from this session will
 * automatically utilize the original authentication that was successfully
 * validated and authorized
 *
 * @author Jeremy Unruh
 */
public abstract class OSClientSession<R, T extends RedfishClient<T>> implements EndpointTokenProvider {

    private static final Logger LOG = LoggerFactory.getLogger(OSClientSession.class);
    @SuppressWarnings("rawtypes")
    private static final ThreadLocal<OSClientSession> sessions = new ThreadLocal<OSClientSession>();

    Config config;
    Set<ServiceType> supports;
    CloudProvider provider;
    EndpointURLResolver fallbackEndpointUrlResolver = new DefaultEndpointURLResolver();
    private Map<String, String> headers;

    @SuppressWarnings("rawtypes")
    public static OSClientSession getCurrent() {
        return sessions.get();
    }

    @SuppressWarnings("unchecked")
    @VisibleForTesting
    public R useConfig(Config config) {
        this.config = config;
        return (R) this;
    }


    /**
     * @return the original client configuration associated with this session
     */
    public Config getConfig() {
        return config;
    }


    public CloudProvider getProvider() {
        return (provider == null) ? CloudProvider.UNKNOWN: provider;
    }

    /**
     * {@inheritDoc}
     */
    public T headers(Map<String, String> headers) {
        this.headers = headers;
        return (T) this;
    }

    public Map<String, String> getHeaders() {
        return this.headers;
    }

    public static class OSClientSessionV1 extends OSClientSession<OSClientSessionV1, RedfishClient.OSClientV1> implements RedfishClient.OSClientV1 {

        String reqId;
        String qsessionid;
        Token token;

        private OSClientSessionV1(Token token, String endpoint,CloudProvider provider, Config config) {
            this.token = token;
            this.config = config;
            this.provider = provider;
            sessions.set(this);
        }

        private OSClientSessionV1(Token token, OSClientSessionV1 parent) {
            this.token = parent.token;
        }

        public static OSClientSessionV1 createSession(Token token) {
            return new OSClientSessionV1(token, token.getEndpoint(), null, null);
        }

        public static OSClientSessionV1 createSession(Token token,CloudProvider provider, Config config) {
            return new OSClientSessionV1(token, token.getEndpoint(), provider, config);
        }

        public String getXRedfishRequestId() {
            return reqId;
        }
        public String getXRedfishCookieId() { return qsessionid; }

        @Override
        public Token getToken() {
            return token;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getEndpoint() {
            return token.getEndpoint();
        }

        @Override
        public SessionService Sessions() {
            return Apis.get(SessionService.class);
        }

        @Override
        public SystemResourceService Systems() {
            return Apis.get(SystemResourceService.class);
        }

        @Override
        public ChassisResourceService Chassis() {
            return Apis.get(ChassisResourceService.class);
        }

        @Override
        public ManagerResourceSerice Managers() {
            return Apis.get(ManagerResourceSerice.class);
        }

        @Override
        public UpdateService Updates() {
            return Apis.get(UpdateService.class);
        }

        @Override
        public AccountService Accounts() {
            return Apis.get(AccountService.class);
        }

        @Override
        public TaskResourceService Tasks() {
            return Apis.get(TaskResourceService.class);
        }

        @Override
        public EventResourceService Events() {
            return Apis.get(EventResourceService.class);
        }

        private String addNATIfApplicable(String url) {
            if (config != null && config.isBehindNAT()) {
                try {
                    URI uri = new URI(url);
                    return url.replace(uri.getHost(), config.getEndpointNATResolution());
                } catch (URISyntaxException e) {
                    LoggerFactory.getLogger(OSClientSessionV1.class).error(e.getMessage(), e);
                }
            }
            return url;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getEndpoint(ServiceType service) {

            final EndpointURLResolver eUrlResolver = (config != null && config.getEndpointURLResolver() != null) ? config.getEndpointURLResolver(): fallbackEndpointUrlResolver;

            return addNATIfApplicable(eUrlResolver.findURLV1(URLResolverParams
                    .create(token, service)
                    .resolver(config != null ? config.getResolver(): null)));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String getTokenId() {
            return token.getId();
        }

        @Override
        public String getETag() {
            return token.getETag();
        }

    }


}
